package cc.chenwh.sequence.client;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Protocol;
import redis.clients.jedis.commands.ProtocolCommand;
import redis.clients.jedis.util.SafeEncoder;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

@SpringBootApplication
public class ClientApplication implements CommandLineRunner {
    private static Logger logger = LoggerFactory.getLogger(ClientApplication.class);

    public static void main(String[] args) {
        SpringApplication.run(ClientApplication.class, args);
    }

    @Value("${sequence-client.host}")
    private String host;

    @Value("${sequence-client.port}")
    private int port;

    @Value("${sequence-client.clientCount}")
    private int clientCount;

    @Value("${sequence-client.sequenceCount}")
    private int sequenceCount;

    @Value("${sequence-client.runningTime}")
    private int runningTime;

    private final List<String> sequenceList = Collections.synchronizedList(new ArrayList<>());

    private static ProtocolCommand cmd = () -> SafeEncoder.encode("sequence");

    private volatile AtomicLong count = new AtomicLong();

    private volatile CountDownLatch latch;

    @Override
    public void run(String... args) throws Exception {
        logger.info("客户端启动：{}：{}，客户端数量：{}，序列数量：{},测试时长分钟：{}", host, port, clientCount, sequenceCount, runningTime);
        CountDownLatch latch = new CountDownLatch(clientCount);

        for (int i = 0; i < sequenceCount; i++) {
            sequenceList.add("test-sequence-" + i);
        }
        final long runningTimeMs = runningTime * 60 * 1000;
        final long startTime = System.currentTimeMillis();
        ExecutorService pool = Executors.newFixedThreadPool(clientCount);
        for (int i = 0; i < clientCount; i++) {
            pool.submit(() -> {
                try {
                    logger.info("线程启动");
                    final Random random = new Random();
                    final Client client = new Client();
                    do {
                        String seqName = sequenceList.get(Math.abs(random.nextInt()) % sequenceCount);
                        Long seq = client.getSequence(seqName);
                        // logger.info("{}=={}",seqName,seq);
                        count.incrementAndGet();
                    }
                    while (System.currentTimeMillis() - startTime < runningTimeMs);
                }
                catch (Exception e) {
                    logger.error(e.getMessage(), e);
                }
                finally {
                    logger.info("线程结束");
                    latch.countDown();
                }
            });
        }

        new Thread(() -> {
            try {
                latch.await();
                pool.shutdown();
                logger.info("时间：{},数量：{}，速度：{}每秒", (System.currentTimeMillis() - startTime), count.get(),
                    (count.get() * 1000) / (System.currentTimeMillis() - startTime));
            }
            catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
    }

    class Client {
        // todo Jedis好像不是线程安全的。 并发的时候，发现服务端的命令无法解析了
        Jedis jedis = new Jedis(host, port, 10000, 10000);

        public Client() {
        }

        long getSequence(String seqName) {
            try {
                byte[] result = (byte[]) jedis.sendCommand(cmd, seqName);
                String rs = new String(result, Protocol.CHARSET);
                return Long.valueOf(rs);
            }
            catch (Exception e) {
                logger.error("调用异常", e);
            }
            return 0;
        }

    }
}
